#ifndef _BASE_SOCKET_H_
#define _BASE_SOCKET_H_

#ifdef WIN32
    #include <stdio.h>
    #include "winsock2.h"
    #include <windows.h>
    #pragma comment(lib, "wininet.lib")
#elif _POSIX_VERSION
    #include <socket.h>
#endif

namespace Maha
{
      namespace Base
      {

#ifdef WIN32

            class WSA : Singleton<WSA>
            {
            public:
                  WSA()
                  {
                        WSADATA wsaData;
                        int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
                        
                        //if (iResult != NO_ERROR)
                        // printf("Error at WSAStartup()\n");
                  }

                  ~WSA()
                  {
                        WSACleanup();
                  }
            };

#endif
            
            class Socket
            {
            public:
                  
                  Socket()
                  {
#ifdef WIN32
                        WSA::GetInstance();
#endif
                        handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                        if (handle == INVALID_SOCKET)
                        {
                              // printf("Error at socket(): %ld\n", WSAGetLastError());
                              throw std::exception(); //cant create socket
                        }
                  }

                  void Bind(string inetAddr, unsigned port)
                  {
                        // The sockaddr_in structure specifies the address family,
                        // IP address, and port for the socket that is being bound.
                        sockaddr_in service;
                        service.sin_family = AF_INET;
                        service.sin_addr.s_addr = inet_addr(inetAddr.c_str());
                        service.sin_port = htons(port);

                        if (bind(handle, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
                        {
                              printf("bind() failed.\n");
                              throw std::exception(); //
                        }
                  }

                  void Connect(string inetAddr, unsigned port)
                  {
                        // The sockaddr_in structure specifies the address family,
                        // IP address, and port for the socket that is being bound.
                        sockaddr_in service;
                        service.sin_family = AF_INET;
                        service.sin_addr.s_addr = inet_addr(inetAddr.c_str());
                        service.sin_port = htons(port);

                        if (connect(handle, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR)
                        {
                              printf("bind() failed.\n");
                              throw std::exception(); //
                        }
                  }

                  void Listen()
                  {
                        // Listen for incoming connection requests 
                        // on the created socket
                        if (listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR)
                        {
                              printf("Error listening on socket.\n");
                              throw std::exception(); //
                        }
                  }

                  template <class InputIterator>
                  InputIterator Write(InputIterator first, InputIterator last)
                  {
                        throw std::exception();
                        return last;
                  }

                  template <class OutputIterator>
                  OutputIterator Read(class OutputIterator)
                  {
                        throw std::Exception();
                        return last;
                  }

                  ~Socket()
                  {
                  }
            private:

#ifdef WIN32
                  SOCKET handle;
#endif
            };

      } // Base
} // Maha
